import base64
import logging
import secrets
import random

from flask_restful import Resource, reqparse, request
from controllers.console import api
from controllers.console.auth.error import (
    InvalidEmailError,
    InvalidTokenError,
    PasswordMismatchError,
    PasswordResetRateLimitExceededError,
)
from controllers.console.setup import setup_required
from extensions.ext_database import db
from libs.helper import email as email_validate
from libs.password import hash_password, valid_password
from models.account import Account
from services.account_service import *
from services.code_log_service import *
from services.errors.account import RateLimitExceededError
from libs.login import login_required

import os
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ClientException
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkcore.auth.credentials import AccessKeyCredential
from aliyunsdkcore.auth.credentials import StsTokenCredential
from aliyunsdkdysmsapi.request.v20170525.SendSmsRequest import SendSmsRequest
from extensions.ext_redis import redis_client
from models.model import *
from datetime import datetime
from sqlalchemy import func


class ForgotPasswordSendEmailApi(Resource):

    @setup_required
    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument('email', type=str, required=True, location='json')
        args = parser.parse_args()

        email = args['email']

        if not email_validate(email):
            raise InvalidEmailError()

        account = Account.query.filter_by(email=email).first()

        if account:
            try:
                AccountService.send_reset_password_email(account=account)
            except RateLimitExceededError:
                logging.warning(f"Rate limit exceeded for email: {account.email}")
                raise PasswordResetRateLimitExceededError()
        else:
            # Return success to avoid revealing email registration status
            logging.warning(f"Attempt to reset password for unregistered email: {email}")

        return {"result": "success"}


class ForgotPasswordCheckApi(Resource):

    @setup_required
    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument('token', type=str, required=True, nullable=False, location='json')
        args = parser.parse_args()
        token = args['token']

        reset_data = AccountService.get_reset_password_data(token)

        if reset_data is None:
            return {'is_valid': False, 'email': None}
        return {'is_valid': True, 'email': reset_data.get('email')}


class ForgotPasswordResetApi(Resource):

    @setup_required
    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument('token', type=str, required=True, nullable=False, location='json')
        parser.add_argument('new_password', type=valid_password, required=True, nullable=False, location='json')
        parser.add_argument('password_confirm', type=valid_password, required=True, nullable=False, location='json')
        args = parser.parse_args()

        new_password = args['new_password']
        password_confirm = args['password_confirm']

        if str(new_password).strip() != str(password_confirm).strip():
            raise PasswordMismatchError()

        token = args['token']
        reset_data = AccountService.get_reset_password_data(token)

        if reset_data is None:
            raise InvalidTokenError()

        AccountService.revoke_reset_password_token(token)

        salt = secrets.token_bytes(16)
        base64_salt = base64.b64encode(salt).decode()

        password_hashed = hash_password(new_password, salt)
        base64_password_hashed = base64.b64encode(password_hashed).decode()

        account = Account.query.filter_by(email=reset_data.get('email')).first()
        account.password = base64_password_hashed
        account.password_salt = base64_salt
        db.session.commit()

        return {'result': 'success'}


class RegisterApi(Resource):

    @setup_required
    def get(self):
        return {"result": "success"}

    @setup_required
    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument('email', type=str, required=True, location='json')
        parser.add_argument('phone', type=str, required=True, location='json')
        parser.add_argument('code', type=str, required=True, location='json')
        args = parser.parse_args()

        if len(args['phone']) <= 10:
            raise ValueError("请输入正确手机号")
        if not email_validate(args['email']):
            raise ValueError("请输入正确邮箱地址")
        if len(args['code']) < 4:
            raise ValueError("请输入正确验证码")

        is_valid = CodeLogService.check(args['phone'], args['code'])

        if is_valid == False:
            raise ValueError("验证码错误")

        account = {
            'email': args['email'],
            'name': args['phone'],
            'interface_language': 'zh-Hans',
            'interface_theme': 'light',
            'password': 'holo123456'
        }
        account = AccountService.create_account(**account);
        CodeLogService.checked(args['phone'])
        return {"result": "success"}

class CodeApi(Resource):

    @setup_required
    def get(self, phone):
        can_send = CodeLogService.can_send(phone)
        if can_send == '0':
            raise ValueError("过于频繁了, 30秒后重试")
        if can_send == '1':
            raise ValueError("每天只能发送5条信息")
        codeLog = CodeLog()
        try:
            client_ip = request.remote_addr
            if request.headers.getlist("X-Forwarded-For"):
                client_ip = request.headers.getlist("X-Forwarded-For")[0]
            elif request.headers.getlist("X-Real-IP"):
                client_ip = request.headers.getlist("X-Real-IP")[0]
            codeLog.ip = client_ip
            codeLog.phone = phone
            codeLog.status = 0
            codeLog.code = random.randint(1000, 9999)
            credentials = AccessKeyCredential('LTAI5tNg7qk1Y9M6qnhByajK', 'iovi8ep0gf4BK73qY3OFsv5H3OJBxr')
            client = AcsClient(region_id='cn-shanghai', credential=credentials)
            req = SendSmsRequest()
            req.set_accept_format('json')
            req.set_SignName("华腾科讯")
            req.set_TemplateCode("SMS_472055144")
            req.set_PhoneNumbers(codeLog.phone)
            json = {
                'code': codeLog.code
            }
            req.set_TemplateParam(json)
            response = client.do_action_with_exception(req)
            codeLog.status = 1
        except Exception as e:
            codeLog.remark = e.args[0]
            codeLog.status = 2
            raise ValueError('服务器异常，请稍后再试')
        codeLog.checked = 0
        codeLog.type = 0
        CodeLogService.save(codeLog)
        return {'result': 'success', 'message': '消息发送成功'}



class CodeLogsApi(Resource):
    @setup_required
    def get(self):
        codeLogs = CodeLogService.list()
        return {'result': 'success', 'codelogs': [{'id': codeLog.id, 'type': codeLog.type, 'phone': codeLog.phone, 'code': codeLog.code, 'checked': codeLog.checked, 'status': codeLog.status, 'reason': codeLog.reason, 'remark': codeLog.remark, 'ip': codeLog.ip, 'created_at': str(codeLog.created_at)} for codeLog in codeLogs]}, 200

class CodeLogPageApi(Resource):
    """Page of code log """
    @setup_required
    @login_required
    def get(self, page, per_page):
        return CodeLogService.get_code_log_page(page, per_page)

api.add_resource(RegisterApi, '/register')
api.add_resource(CodeApi, '/code/<string:phone>')
api.add_resource(CodeLogsApi, '/codelog/list')
api.add_resource(CodeLogPageApi, '/codelog/page/<int:page>/<int:per_page>')
api.add_resource(ForgotPasswordSendEmailApi, '/forgot-password')
api.add_resource(ForgotPasswordCheckApi, '/forgot-password/validity')
api.add_resource(ForgotPasswordResetApi, '/forgot-password/resets')
